package com.pureland.storage.utils;


import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tika.mime.MimeType;
import org.apache.tika.mime.MimeTypeException;
import org.apache.tika.mime.MimeTypes;

import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;

/**
 * @projectName: pureland_cloud
 * @className: com.pureland.storage.utils.FileUtils
 * @description: 文件工具类
 * @author: tong.li
 * @createTime: 2020/10/19 15:48
 * @version: v1.0
 * @copyright: 版权所有 李彤 © 2020
 */
@Slf4j
public class FileUtils {

    /**
     * 未知的内容的大小
     */
    public  static long DEFAULT_CONTENT_LENGTH_UNKOWN = -1L;

    /**
     * 默认的Content-Type为流类型
     */
    public  static  String DEFAULT_CONTENT_TYPE = "application/octet-stream";


    /**
     * 根据旧的名称生成新的名称
     *
     * @param originName originName
     * @return 生成结果
     */
    public static String randomName(String originName) {
        final String uuid = UUID.randomUUID().toString();
        if (StringUtils.isBlank(originName)) {
            return uuid;
        }
        final String extension = FilenameUtils.getExtension(originName);
        return uuid + "." + extension;
    }

    public static final String SEPARATOR = "/";


    public static String targetName(boolean random, String prefix, String originName) {
        if (!random) {
            return StringUtils.join(SEPARATOR, originName);
        }
        final String name = randomName(originName);
        if (StringUtils.isNotBlank(name)) {
            return StringUtils.isBlank(prefix) ? StringUtils.join(SEPARATOR, name) : StringUtils.join(SEPARATOR, prefix, name);
        }
        return StringUtils.isBlank(prefix) ? StringUtils.join(SEPARATOR, originName) : StringUtils.join(SEPARATOR, prefix, originName);
    }


    /**
     * 从本地文件获取文件信息
     * @param file
     * @param isNeedFileStream
     * @return
     */
    public static FileContentInfo getLocalFileContentInfo(File file, boolean isNeedFileStream) {
        FileInputStream is = null;
        try {
            // 文件信息校验
            if (file == null || !file.exists() || file.isDirectory()) {
                new IllegalArgumentException("文件信息有误(文件为空/为目录/不存在),请检测文件!");
            }
            String contentType = new MimetypesFileTypeMap().getContentType(file);
            contentType = StringUtils.isEmpty(contentType) ? FileUtils.DEFAULT_CONTENT_TYPE : contentType;
            String extension = getFileExtension(contentType);
            log.info("getLocalFileContentInfo, File ContentType is : {},  File Extension is : {}" ,  contentType, extension);
            long length = file.length();
            FileContentInfo fileContentInfo = FileContentInfo.builder()
                    .mimeTypeName(contentType)
                    .contentSize(length)
                    .extension(extension).build();
            // 因为流是宝贵资源,可根据是否需要选择性构建流资源
            if (isNeedFileStream) {
                is = new FileInputStream(file);
                fileContentInfo.setInputStream(is);
            }
            if (length == 0 && is != null) {
                fileContentInfo.setContentSize(is.available());
            }
            return fileContentInfo;
        } catch (Exception e) {
            log.error("get getLocalFileContentInfo error, file path: {}", file.getAbsolutePath(),e);
            try {
                IOUtils.close(is);
            } catch (IOException ex) {
                log.error("getLocalFileContentInfo IOUtils.close(bis) error", ex);
            }
        }
        throw new RuntimeException("getLocalFileContentInfo error");
    }


    /**
     * 从远程地址获取文件信息
     * 注意: inputStream.available()获取到文件大小不一定准确。具体参考:
     *     1. https://blog.csdn.net/zyxwvuuvwxyz/article/details/78549923
     *     2. https://www.cnblogs.com/fanhq/p/5233718.html
     *     3. https://github.com/minio/minio-java/issues/1081 本人提的issue
     *
     * @param urlPath
     * @param isNeedFileStream
     * @return
     */
    public static FileContentInfo getRemoteFileContentInfo(String urlPath, boolean isNeedFileStream) {
        BufferedInputStream bis = null;
        try {
            // 远程地址校验
            if (StringUtils.isEmpty(urlPath) || !urlPath.startsWith("http://") || !urlPath.startsWith("https://")) {
                new IllegalArgumentException("远程地址有误(地址为空/非http/非https),请检测地址!");
            }
            URL url = new URL(urlPath);
            HttpURLConnection urlconnection = null;
            urlconnection = (HttpURLConnection) url.openConnection();
            urlconnection.connect();
            url.openStream();
            bis = new BufferedInputStream(url.openStream());
            String contentType = HttpURLConnection.guessContentTypeFromStream(bis);
            contentType = StringUtils.isEmpty(contentType) ? FileUtils.DEFAULT_CONTENT_TYPE : contentType;
            String extension = getFileExtension(contentType);
            int length = urlconnection.getContentLength();
            FileContentInfo fileContentInfo = FileContentInfo.builder()
                    .mimeTypeName(contentType)
                     // 设置大小
                    .contentSize(length)
                    .extension(extension).build();
            // 因为流是宝贵资源,可根据是否需要选择性构建流资源
            if (isNeedFileStream) {
                fileContentInfo.setInputStream(bis);
            }
            if (length == 0 && bis != null) {
                fileContentInfo.setContentSize(bis.available());
            }
            return fileContentInfo;
        } catch (Exception e) {
            log.error("get getRemoteFileContentInfo error, urlPath path: {}", urlPath,e);
            try {
                IOUtils.close(bis);
            } catch (IOException ex) {
                log.error("getRemoteFileContentInfo IOUtils.close(bis) error", ex);
            }
        }
        throw new RuntimeException("getRemoteFileContentInfo error");
    }


    public static FileContentInfo getInputStreamFileContentInfo(InputStream inputStream) {
        try {
            // 远程地址校验
            if (inputStream == null) {
                new IllegalArgumentException("inputStream is not null");
            }
            String contentType = HttpURLConnection.guessContentTypeFromStream(inputStream);
            contentType = StringUtils.isEmpty(contentType) ? FileUtils.DEFAULT_CONTENT_TYPE : contentType;
            String extension = getFileExtension(contentType);
            int length = inputStream.available();
            FileContentInfo fileContentInfo = FileContentInfo.builder()
                    .mimeTypeName(contentType)
                    // 设置大小
                    .contentSize(length)
                    .extension(extension).build();
            // 因为流是宝贵资源,可根据是否需要选择性构建流资源
            fileContentInfo.setInputStream(inputStream);
            return fileContentInfo;
        } catch (Exception e) {
            log.error("get getInputStreamFileContentInfo error}",e);
            try {
                IOUtils.close(inputStream);
            } catch (IOException ex) {
                log.error("getInputStreamFileContentInfo IOUtils.close(bis) error", ex);
            }
        }
        throw new RuntimeException("getInputStreamFileContentInfo error");
    }


    private static String getFileExtension(String contentType) throws MimeTypeException {
        String ext = "";
        if (StringUtils.isEmpty(contentType)) {
            return ext;
        }
        // 获取文件扩展名
        try {
            MimeTypes allTypes = MimeTypes.getDefaultMimeTypes();
            MimeType mimeType = allTypes.forName(contentType);
            ext =  mimeType.getExtension();
        } catch (MimeTypeException e) {
            log.error("getFileExtension error, contentType is {]",contentType);
        }
        return ext;
    }


    /**
     * 文件内容信息
     */
    @Data
    @Builder
    public static class FileContentInfo {


        /**
         * mimeTypeName
         */
        private String mimeTypeName;

        /**
         * 扩展名
         */
        private String extension;


        /**
         * 文件流(可选择)
         */
        private InputStream inputStream;


        /**
         * 内容大小
         */
        private long contentSize;

    }



}